
#include <include/sdk/PPlayerInfo.h>
#include "LogicExecutor.h"
#include "sdk/misc.h"
#include "sdk/PlayerInfo.h"

using namespace std;

std::mutex pushDataMutex;
std::mutex feedbackPushDataMutex;
std::mutex showInfoMutex;
std::mutex mainCmdMutex;

LogicExecutor::LogicExecutor() {
    game = misc::make_unique<Game>();
    for (int i = 0; i < 2; ++ i)
        act[i] = make_shared<NullActMaker<const PlayerInfo &, Commands &, SDK::PShowInfo &> >();
    for (int i = 0; i < 2; ++ i)
        feedback[i] = make_shared<FeedbackNullActMaker>();
    game->initPlant();
    main_cmd_timestamp = -1;
}


PlayerInfo LogicExecutor::gatherInfo(PlayerInfo &playerInfo, int playerId) {
    PlayerInfo p = playerInfo;
    pushDataMutex.lock();
    p.push_data.insert(p.push_data.end(), pushData[playerId - 1].begin(), pushData[playerId - 1].end() );
    pushData[playerId - 1].clear();
    pushDataMutex.unlock();
    return p;
}

Commands LogicExecutor::fetchCommand(int playerId) {
    PlayerInfo playerinfo = this->game->getPlayerInfo(playerId);
    PlayerInfo info = gatherInfo(playerinfo, playerId);
    SDK::PShowInfo pShowInfo;
    Commands cmd, execCmd;
    this->act[playerId - 1]->getCommand(info, cmd, pShowInfo);
    showInfoMutex.lock();
    showInfo.merge(pShowInfo);
    showInfoMutex.unlock();
    for (auto i: cmd.cmds){
        if (i.name != "UseSkill") continue;
        Entity* entity = game->entityManager.findEntityByID(i.params[0]);
        if (entity == nullptr) continue;
        if (entity->properties["team"] != std::to_string(playerId)) continue;
        execCmd.cmds.push_back(i);
    }
    return execCmd;
}

void LogicExecutor::fetchCommands(int PlayerId) {
    mainCmdMutex.lock();
    if (main_cmd_timestamp != game->nowRound){
        main_cmd_timestamp = game->nowRound;
        main_cmd.clear();
    }
    main_cmd.merge(fetchCommand(PlayerId));
    mainCmdMutex.unlock();
}

void LogicExecutor::sendCommand(Commands pCommand) {
    mainCmdMutex.lock();
    if (main_cmd_timestamp != game->nowRound){
        main_cmd_timestamp = game->nowRound;
        main_cmd.clear();
    }
    main_cmd.merge(pCommand);
    mainCmdMutex.unlock();
}

int LogicExecutor::oneRoundRun() {
    cerr << "round: " << game->nowRound + 1 << " started" << endl;
    game->roundPreparation(main_cmd);
    game->oneRoundRun();
    int winner = game->getWinner();
    game->roundClean();
    cerr << "round: " << game->nowRound << " finished" << endl;
    return winner;
}


void LogicExecutor::pushInfo(int playerId, std::string data) {
    pushDataMutex.lock();
    pushData[playerId - 1].push_back(data);
    pushDataMutex.unlock();
}

void LogicExecutor::pushInfoFeedback(int playerId, std::string data){
    feedbackPushDataMutex.lock();
    feedbackPushData[playerId - 1].push_back(data);
    feedbackPushDataMutex.unlock();
}

void LogicExecutor::invokeFeedback(int playerId) {
    feedbackPushDataMutex.lock();
    SDK::PShowInfo pShowInfo;
    vector<SDK::PPlayerInfo::HumanOrder> humanOrders = SDK::PPlayerInfo::readHumanOrders(feedbackPushData[playerId - 1]);
    feedbackPushData[playerId - 1].clear();
    this->feedback[playerId - 1]->getCommand(humanOrders, pShowInfo);
    showInfoMutex.lock();
    showInfo.merge(pShowInfo);
    showInfoMutex.unlock();
    feedbackPushData[playerId - 1].clear();
    feedbackPushDataMutex.unlock();
}

std::string LogicExecutor::fetchShowInfo(){
    showInfoMutex.lock();
    std::string info = jsonToString(showInfo.toJson());
    showInfo.cmds.clear();
    showInfoMutex.unlock();
    return info;
}
